package wsi.gui.resolve.model;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.rygh.core.util.UIo;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlParser;
import org.rygh.core.xml.XmlPath;
import org.rygh.core.xml.XmlTree;
import org.rygh.semantic.model.rdf.Entity;

import wsi.exp.Exp1SelectedTree;
import wsi.exp.Exp2TreeToEntity;
import wsi.exp.exp5.JudgeFile;
import wsi.exp.exp5.JudgeFileManager;
import wsi.function.DirManager;
import wsi.function.Function;
import wsi.match.MatchProperty;
import wsi.match.XmlFunctionalityController;

public class JudgeItem {

	private final List<MatchInfoData> tableDataList = new ArrayList<>();
	private final List<XmlTree> xmlTreeList = new ArrayList<>();
	private final JudgeFile judgeFile;

	private final File xmlFile;
	private final Function function;
	private final XmlPath subrootPath;
	private final MatchProperty matchProperty;

	private int prevIndex = 0;
	private int index = 0;
	private boolean valid = true;
	private boolean iterateAll = false;

	public JudgeItem(Function function, File xmlFile, File jfile) {
		this.function = function;
		this.xmlFile = xmlFile;

		load();

		if (jfile != null)
			this.judgeFile = new JudgeFile(jfile);
		else
			this.judgeFile = new JudgeFile(size());

		subrootPath = Exp1SelectedTree.getFunctionSubrootPath(function);
		matchProperty = Exp2TreeToEntity.getFunctionMatchProperty(function)
				.get(0);
	}

	/**
	 * after moving index
	 * 
	 * @return
	 */
	public boolean checkIndex() {
		return index == size();
	}

	/**
	 * before moving index
	 * 
	 * @return
	 */
	public MatchInfoData get() {
		return tableDataList.get(index);
	}

	/**
	 * after moving index
	 * 
	 * @return
	 */
	public String getComputeProbString() {
		StringBuilder sb = new StringBuilder();

		double xml_comp_inv_func, pred_inv_func, conf, prob_sub, prob = 1;
		double probPositiveValidMatch;

		MatchInfoData data = tableDataList.get(prevIndex);

		boolean first = true;

		for (MatchInfoTuple i : data.getData()) {
			xml_comp_inv_func = i.getXpathFunctionality();
			pred_inv_func = i.getPredicatePathFunctionality();
			conf = 1;

			assert xml_comp_inv_func >= 0 && xml_comp_inv_func <= 1;
			assert pred_inv_func >= 0 && pred_inv_func <= 1;
			assert conf >= 0 && conf <= 1;

			prob_sub = xml_comp_inv_func * pred_inv_func * conf;
			prob *= (1 - prob_sub);

			sb.append(String.format("%s(1 - %.2f * %.2f)\n", (first ? ""
					: "  * "), xml_comp_inv_func, pred_inv_func));

			first = false;
		}

		probPositiveValidMatch = 1 - prob;

		return String.format("%.2f = \n1 - %s", probPositiveValidMatch,
				sb.toString());
	}

	public File getFile() {
		return xmlFile;
	}

	public Function getFunction() {
		return function;
	}

	public int getIndex() {
		return index;
	}

	/**
	 * before moving index
	 * 
	 * @return
	 */
	public Boolean getJudgment() {
		return judgeFile.get(index);
	}

	public MatchProperty getMatchProperty() {
		return matchProperty;
	}

	private int getNextSlot(int index) {
		int s = size();

		if (index >= s)
			return s;

		int i;
		for (i = index; i < s; i++)
			if (judgeFile.get(i) == null)
				return i;

		return i;
	}

	public XmlPath getSubrootPath() {
		return subrootPath;
	}

	/**
	 * after moving index
	 * 
	 * @return
	 */
	public XmlTree getXmlTree() {
		return xmlTreeList.get(prevIndex);
	}

	public boolean isEmpty() {
		return tableDataList.isEmpty();
	}

	public boolean isValid() {
		return valid;
	}

	private void load() {
		XmlParser parser = new XmlParser(UIo.readFile(xmlFile));
		XmlTree tree = parser.parse();
		assert parser.isValid();

		List<XmlLeaf> nodes = tree.getNodesByXPath("t:matched_entity");
		XmlFunctionalityController xmlfunc = new XmlFunctionalityController(
				new DirManager(function));
		xmlfunc.load();

		MatchInfoTuple tuple;
		MatchInfoData data;
		XmlTree subtree;
		XmlObject obj;

		Set<XmlLeaf> parentSet = new HashSet<>();

		for (XmlLeaf n : nodes) {
			if (parentSet.contains(n.getParent())) {
				System.out.printf(
						"[warn] duplicated appended children @ %s, %s\n",
						function.getFullName(), n.getParent().getXpath());
				continue;
			}

			parentSet.add(n.getParent());
			obj = n.getValue();

			data = new MatchInfoData();

			Entity entity = new Entity(obj.getAttribute("t:url"), false);

			data.setEntity(entity);

			String prob_str = obj.getAttribute("t:prob");

			if (prob_str != null)
				data.setProbability(Double.parseDouble(prob_str));

			if (!n.isLeaf())
				for (XmlLeaf c : n.getChildren()) {
					tuple = new MatchInfoTuple(c, xmlfunc);

					data.add(tuple);
				}

			tableDataList.add(data);

			subtree = new XmlTree();
			subtree.getRoot().addChild(n.getParent());

			xmlTreeList.add(subtree);
		}
	}

	public void moveIndex() {
		prevIndex = index;

		if (iterateAll)
			index++;
		else
			index = getNextSlot(index + 1);
	}

	public void resetIndex() {
		if (iterateAll)
			prevIndex = index = 0;
		else
			prevIndex = index = getNextSlot(0);
	}

	public void save() {
		if (judgeFile.size() != size()) {
			throw new RuntimeException(String.format(
					"[info] not save %d != %d\n", judgeFile.size(), size()));
		}

		String fn = UIo.getFileNameWithoutExtension(xmlFile.getName());
		String fname = String.format("%s%s.txt", fn, JudgeFileManager._JUDGED);
		File file = new File(xmlFile.getParent(), fname);

		// System.out.printf("[info] save %s\n", file.getAbsolutePath());

		UIo.saveFile(file, judgeFile.toString());
	}

	public void setIterateAll(boolean iterateAll) {
		this.iterateAll = iterateAll;
	}

	/**
	 * after moving index
	 * 
	 * @return
	 */
	public void setResult(boolean b) {
		judgeFile.set(prevIndex, b);
	}

	public int size() {
		return tableDataList.size();
	}

}
